﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using Lite.Common;
using System.Threading;

namespace Lite.Net
{
    public abstract partial class SocketMonitorBase
        : IDisposable
    {
        private SocketAsyncEventArgs asyncEventArgs;

        private bool _isDisposed;
        private bool _isRunning;
        private bool _isPaused;
        private bool _isListening;

        private readonly object _stateLockObject = new object();

        protected SocketMonitorBase()
        {
            asyncEventArgs = new SocketAsyncEventArgs();
            asyncEventArgs.Completed+=new EventHandler<SocketAsyncEventArgs>(HandleCompletedOperation);
        }

        ~SocketMonitorBase()
        {
            Dispose(false);
        }

        private void BeginOperation()
        {
            bool lastOpWasSynchronous = true;

            do
            {
                lock (_stateLockObject)
                {
                    if (!_isRunning || _isPaused || _isListening)
                        return;

                    _isListening = true;
                }

                lastOpWasSynchronous = !BeginAsyncOperation(asyncEventArgs);

                if (lastOpWasSynchronous)
                {
                    lock (_stateLockObject)
                    {
                        _isListening = false;

                        //TODO: Operation results should be buffered if the monitor is currently paused or stopped
                        if (!_isRunning || _isPaused)
                            return;
                    }

                    if (!CompleteAsyncOperation(asyncEventArgs))
                    {
                        return;
                    }
                }
            } while (lastOpWasSynchronous);
        }

        private void HandleCompletedOperation(object sender, SocketAsyncEventArgs e)
        {
            lock (_stateLockObject)
            {
                _isListening = false;
            }
            
            //TODO: Operation results should be buffered if the monitor is currently paused or stopped
            if (!_isRunning || _isPaused)
                return;

            if (CompleteAsyncOperation(asyncEventArgs))
                BeginOperation();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="eventArgs"></param>
        /// <returns>
        /// True if the operation is executing asynchronously; otherwise false.
        /// </returns>
        protected abstract bool BeginAsyncOperation(SocketAsyncEventArgs eventArgs);

        /// <summary>
        /// </summary>
        /// <returns>
        /// True if the connection is still open and polling should
        /// continue, otherwise false.
        /// </returns>
        protected abstract bool CompleteAsyncOperation(SocketAsyncEventArgs eventArgs);

        protected virtual void Dispose(bool isDisposing)
        {
            if (!_isDisposed)
            {
                if (isDisposing)
                {
                    Halt();
                    asyncEventArgs.Dispose();
                }

                _isDisposed = true;
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }

        #endregion
    }

    public abstract partial class SocketMonitorBase
        : IStartHaltPauseRestart
    {
        public virtual void Start()
        {
            lock (_stateLockObject)
            {
                _isRunning = true;
            }

            BeginOperation();
        }

        public virtual void Halt()
        {
            lock (_stateLockObject)
            {
                _isRunning = false;
                _isPaused = false;
            }
        }

        public StartHaltState State
        {
            get
            {
                lock (_stateLockObject)
                {
                    if (_isRunning)
                    {
                        if (_isPaused)
                        {
                            return StartHaltState.Paused;
                        }
                        else
                        {
                            return StartHaltState.Started;
                        }
                    }
                    else
                    {
                        if (_isListening)
                        {
                            return StartHaltState.Halting;
                        }
                        else
                        {
                            return StartHaltState.Halted;
                        }
                    }
                }
            }
        }

        public void Pause()
        {
            lock (_stateLockObject)
            {
                if (!_isRunning)
                {
                    throw new InvalidOperationException("Cannot pause an instance that is not started.");
                }
                else
                {
                    _isPaused = true;
                }
            }
        }

        public void Restart()
        {
            lock (_stateLockObject)
            {
                if (!_isRunning)
                {
                    throw new InvalidOperationException("Cannot pause an instance that is not started.");
                }
                else
                {
                    _isPaused = false;
                }
            }

            BeginOperation();
        }
    }
}
